package core

import (
	"fmt"
)

// 传递进度条信息的结构体
type Msg struct {
	Rs   bool   // 是否读完
	Line int    // 当前行数
	Size int    // 进度条的进度
	Max  int    // 最大进度，文件大小，上限等相关信息
	Info string // 相关元信息，如文件名等
}

var (
	// 进度条最大条数
	MaxNum = 1
	// 受限于控制台显示的行数，设置合理的最大值。
	currentNum  = 1
	displayFlag = 1
	// 显示信息的行的元信息，行号和对应的标签
	LineMsg map[string]int
	// 待使用的行号的chan
	LineNumChan chan int
	// 需要进度条显示的信息结构体通道
	MgCh chan Msg
)

// IniBar 初始化进度条函数
// maxNum 进度条最大条数
// lineMsg 显示信息的行的元信息，行号和对应的标签
// 同时初始化MgCh 的容量和maxNum一致
func InitBar(maxNum int, lineMsg chan<- int) {
	MaxNum = maxNum
	displayFlag = 1 << (MaxNum + 1)
	// 显示信息的行的元信息，行号和对应的标签
	LineMsg = make(map[string]int, MaxNum)
	// 待使用的行号的chan
	LineNumChan = make(chan int, MaxNum)
	for i := 0; i < MaxNum; i++ {
		LineNumChan <- i
	}
	MgCh = make(chan Msg, MaxNum)
}

// 移动光标到指定的行,line行是从1开始计算
func move(line int) {
	// 每当新出现一行显示数据时，首行除外（因为执行命令是按的确认键，会新建一行）
	if displayFlag&(1<<(line-1)) != 1<<(line-1) && line != 1 {
		regist(line)
		// 将已经写了的行注册到displayFlag中,注册后再显示的最后一行后面添加一行
		// 先从最下面移动到最上面，在向下移动到指定的行\033[100D
		fmt.Printf("\033[%dA\033[%dB", currentNum, line-1)
		fmt.Println()
		currentNum = line
		return
	}
	// 先从最下面移动到最上面，在向下移动到指定的行
	fmt.Printf("\033[%dA\033[%dB", currentNum, line)
	// fmt.Println()
	currentNum = line
}

func printf(line int, format string, a ...interface{}) {
	move(line)
	fmt.Printf(format, a...)
}

func regist(line int) {
	displayFlag = displayFlag | 1<<(line-1)
}

// DisplayBar显示进度条函数
// ch 进度条显示的信息结构体通道，有信息对应的行号和信息，
// graph 进度条显示用的字符
func DisplayBar(ch <-chan Msg, graph, format string) {
	defer move(MaxNum + 1)
	str := ""
	for {
		select {
		case ms := <-ch:
			str = ""
			percent := ms.Size * 100 / ms.Max
			for i := percent; i > 0; i -= 2 {
				str += graph //更新进度条
			}
			printf(ms.Line, format, str, ms.Size, float64(ms.Size), "kb", float64(ms.Max), "kb", float64(percent), ms.Info)
			if ms.Rs {
				return
			}
		default:
			continue
		}
	}
}
